﻿using System;
using Microsoft.ReportingServices.Interfaces;
using Microsoft.ReportingServices.DataProcessing;
using System.Management.Automation;
using System.Security.Principal;

namespace PowerShellDataProcessingExtension
{
    /// <summary>
    /// Represents additional connection properties that can be implemented by 
    /// Reporting Services data processing extensions regarding security and authentication.
    /// </summary>
    /// <remarks>
    /// The IDbConnectionExtension interface enables you to implement an extension of the 
    /// IDbConnection interface, which adds additional functionality for authenticating a 
    /// connection to a data source. The IDbConnectionExtension interface can be used to build 
    /// a custom data processing extension. For more information about Connection classes, 
    /// see Implementing a Connection Class for a Data Processing Extension.
    /// An application does not create an instance of the IDbConnectionExtension interface 
    /// directly, but creates an instance of a class that implements IDbConnectionExtension.  
    /// Classes that implement IDbConnectionExtension must also implement all required members, 
    /// and typically define additional members to add provider-specific functionality. 
    /// Because IDbConnectionExtension inherits from IDbConnection, you must also implement all 
    /// of the IDbConnection members as part of your Connection class including those members from IExtension.
    /// </remarks>
    public class PSDBConnectionExtension : IDbConnectionExtension,IExtension
    {
        private String connectionString;
        private String userName;
        private String password;
        private String impersonate;
        private WindowsIdentity connectionUser = null;
        private Boolean integratedSecurity = false;
        private Boolean connectionOpened = false;
        private System.Data.ConnectionState connectionState = System.Data.ConnectionState.Closed;

        /// <summary>
        /// Sets the username of the user that is impersonated while queries are executed. 
        /// This property is ignored by the report server if impersonation is not supported by the data provider.
        /// </summary>
        public String Impersonate
        {
            set { this.impersonate = value; }
        }

        /// <summary>
        /// Indicates whether the connection should use integrated security rather than pass in a username and password.
        /// </summary>
        public Boolean IntegratedSecurity
        {
            get
            {
                return this.integratedSecurity;
            }
            set
            {
                this.integratedSecurity = value;
            }
        }

        /// <summary>
        /// Gets or sets the password to use when connecting to the database. 
        /// Overrides any password specified in the connection string.
        /// </summary>
        public String Password
        {
            set { this.password = value; }
        }

        /// <summary>
        /// Gets or sets the username to use when connecting to the database. 
        /// Overrides any username specified in the connection string.
        /// </summary>
        public String UserName
        {
            set { this.userName = value; }
        }


        public IDbTransaction BeginTransaction()
        {
            throw new NotImplementedException();
        }

        public String ConnectionString
        {
            get
            {
                // Always return exactly what the user set.
                return this.connectionString;
            }
            set
            {
                this.connectionString = value;
            }
        }

        public int ConnectionTimeout
        {
            get 
            {
                // Returns the connection time-out value set in the connection
                // string. Zero indicates an indefinite time-out period.
                return 0;
            }
        }

        public IDbCommand CreateCommand()
        {            
            return new PSDBCommand(this);
        }

        public IDbCommand CreateCommand(String commandText)
        {
            return new PSDBCommand(this, commandText);
        }

        /// <summary>
        /// Opens a database connection with the settings specified by the ConnectionString property of the provider-specific Connection object.
        /// </summary>
        public void Open()
        {
            /// Open the connection and set the ConnectionState property. 
            if (!this.connectionOpened)
            {
                if (this.integratedSecurity)
                {
                    this.connectionUser = WindowsIdentity.GetCurrent();
                }
                else
                {
                    this.connectionUser = null;
                }

                this.connectionOpened = true;
            }

        }

        /// <summary>
        /// Closes the connection to the database.
        /// </summary>
        public void Close()
        {
            /// Close the  connection and set the ConnectionState property
            if (this.connectionUser != null)
            {
                this.connectionUser.Dispose();
            }
            this.connectionOpened = false;

        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public String LocalizedName
        {
            get { return "PowerShell DPE"; }
        }

        public void SetConfiguration(string configuration)
        {
            //throw new NotImplementedException();
        }

        /// <summary>
        /// Internal to return the user
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        internal WindowsIdentity ConnectionUser
        {
            get
            {
                // this.connectionOpened is valid only during open connection
                if (!this.connectionOpened)
                {
                    // TODO something intelligent here
                    throw new Exception("oh no you didn't!");
                }
                return this.connectionUser;
            }
        }
    }
}
